/*
 * Copyright (C) 2011 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.collect;

import com.google.common.annotations.GwtCompatible;
import com.google.j2objc.annotations.WeakOuter;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Workaround for <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6312706"> EnumMap
 * bug</a>. If you want to pass an {@code EnumMap}, with the intention of using its
 * {@code entrySet()} method, you should wrap the {@code EnumMap} in this class instead.
 *
 * <p>
 * This class is not thread-safe even if the underlying map is.
 *
 * @author Dimitris Andreou
 */
@GwtCompatible
final class WellBehavedMap<K, V> extends ForwardingMap<K, V> {
    private final Map<K, V> delegate;
    private Set<Entry<K, V>> entrySet;

    private WellBehavedMap(Map<K, V> delegate) {
        this.delegate = delegate;
    }

    /**
     * Wraps the given map into a {@code WellBehavedEntriesMap}, which intercepts its
     * {@code entrySet()} method by taking the {@code Set<K> keySet()} and transforming it to
     * {@code Set<Entry<K, V>>}. All other invocations are delegated as-is.
     */
    static <K, V> WellBehavedMap<K, V> wrap(Map<K, V> delegate) {
        return new WellBehavedMap<K, V>(delegate);
    }

    @Override
    protected Map<K, V> delegate() {
        return delegate;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        Set<Entry<K, V>> es = entrySet;
        if (es != null) {
            return es;
        }
        return entrySet = new EntrySet();
    }

    @WeakOuter
    private final class EntrySet extends Maps.EntrySet<K, V> {
        @Override
        Map<K, V> map() {
            return WellBehavedMap.this;
        }

        @Override
        public Iterator<Entry<K, V>> iterator() {
            return new TransformedIterator<K, Entry<K, V>>(keySet().iterator()) {
                @Override
                Entry<K, V> transform(final K key) {
                    return new AbstractMapEntry<K, V>() {
                        @Override
                        public K getKey() {
                            return key;
                        }

                        @Override
                        public V getValue() {
                            return get(key);
                        }

                        @Override
                        public V setValue(V value) {
                            return put(key, value);
                        }
                    };
                }
            };
        }
    }
}
